import { Vector3 } from "@/utils/Vector3"
import { Group } from "@/js/components/Group"
import { GraphView } from "@/js/components/GraphView"
import { LinkNode } from "../../LinkNode"
import { AbsoluteLayout, LinearLayout } from "../../Layout"
import { Text } from "../../Text"
import { Menu } from "@/utils/Menu"
import { InputLinkNodeFlow, InputLinkNode } from "../../InputLinkNode"
import { CustomElement } from "../../CustomElement"
import ParserError from "@/js/GraphParser/src/ParserError"

export class DefaultLayout  {
    container = new LinearLayout()
    header = new LinearLayout()
    mainContainer = new LinearLayout()

    headers?: {
        left: LinearLayout,
        right: LinearLayout,
        enter: InputLinkNodeFlow
        output: InputLinkNodeFlow
    }
    graphView: GraphView
    target: GraphNode

    main?: {
        left: LinearLayout,
        right: LinearLayout,
    }

    showLeftFlow: boolean
    showRightFlow: boolean
    headerBgColor: string
    constructor(
        target: GraphNode, 
        graphView: GraphView, 
        showLeftFlow = true, 
        showRightFlow = true,
        headerBgColor = "#2196f3"
    ) {
        this.target = target
        this.graphView = graphView
        this.showLeftFlow = showLeftFlow
        this.showRightFlow = showRightFlow
        this.headerBgColor = headerBgColor

        this.container.background("rgba(60,60,60,0.9)")

        this.initHeader()
        this.initMainContainer()

        this.container.column()
        this.container.appendChild(this.mainContainer)

        this.container.setStyle({
            "border-radius": "8px",
            "overflow": "hidden",
        })
    }

    initHeader() {
        const headerLeft = new LinearLayout()
        headerLeft.itemsCenter()
        const headerRight = new LinearLayout()
        headerRight.itemsCenter().justifyEnd().flex(1).margin("0 0 0 20px")
        this.header.appendChild(headerLeft)
        this.header.appendChild(headerRight)
        this.container.appendChild(this.header)
        this.header.background(`linear-gradient(90deg, ${this.headerBgColor}, #ffffff22)`)
        this.header.padding("0px 5px")
        this.header.minWidth("100px")

        // 入节点
        const enter = new InputLinkNodeFlow(this.target, this.graphView, {
            name: this.target.name || this.target.ClassName ,
            model: 'entry',
            showLinkNode: this.showLeftFlow
        })
        headerLeft.appendChild(enter)
        this.showLeftFlow && this.target.addEnter("enter", enter.linkNode as LinkNode)

        // 出节点
        const output = new InputLinkNodeFlow(this.target, this.graphView, {
            name: "output",
            model: "output",
            showDesc: false,
            showLinkNode: this.showRightFlow
        })
        headerRight.appendChild(output)
        this.showRightFlow && this.target.addOutput("output", output.linkNode as LinkNode)

        this.headers = {
            left: headerLeft,
            right: headerRight,
            enter,output
        }


        this.header.addEventListener("mousedown", ( e:MouseEvent)=>{
            const { button, clientX, clientY } = e
            if(button === 2){
                const list = [
                    {
                        name: this.target.name || this.target.ClassName + "节点",
                        items: [
                            {
                                style:{
                                    fontSize: '12px',
                                    minWidth: '100px',
                                    padding: '2px 5px'
                                },
                                name: "删除",
                                click: ({close}:any)=>{
                                    this.target.delete()
                                    close()
                                }
                            }
                        ]
                    }
                ]
        
                new Menu(list).show(clientX, clientY)

                e.stopPropagation()
            }
        })
    }

    initMainContainer() {
        this.mainContainer.padding("5px").justifyBetween()
        const left = new LinearLayout().column()
        const right = new LinearLayout().column().margin("0 0 0 30px")
        this.mainContainer.appendChild(left)
        this.mainContainer.appendChild(right) 

        this.main = {
            left: left,
            right: right,
        }
    }
}

export class GraphNode extends Group {
    data = {}
    enters: { [k: string]: LinkNode } = {}
    outputs: { [k: string]: LinkNode } = {}
    inputModels:{
        outputs: {[k:string]: InputLinkNode }
        enters: {[k:string]: InputLinkNode }
    } = {
        outputs: {},
        enters: {}
    }

    userData = {
        outputs: {},
        enters: {}
    }

    graphView?: GraphView

    name = ""
    static  menuName?:string

    DefaultLayout = DefaultLayout

    get isGraphNode(){return true}

    static parser?: (obj: ParserOptions) => string = ({node}) =>{
        throw new ParserError(node.uuid, "！解析错误：当前节点还未提供解析函数")
    }

    error?: AbsoluteLayout

    attach(graphView: GraphView) {
        this.graphView = graphView
        graphView.add(this)
        this.load()
        
        return this
    }

    remove(){
        if(this.graphView){
            this.graphView?.removeChild(this)
            const index = this.graphView?.list.findIndex(this as any)
            this.graphView?.list.splice( index , 1)
        }
    }

    // 初始化
    load() {
        this.defaultStyle()
    }

    defaultStyle() {
        this.setStyle({
            // "border": "solid 2px transparent",
            "position": "absolute",
            "left": "0",
            "top": "0",
            "z-index": 1,
            "color": "#fff",
            "transition": "border 0.25s",
            "box-sizing": "border-box",
            "border-radius": "8px",
        })
        .transition("outline 0.25s")
        .outline("solid 2px transparent")
    }

    /**
     * 导入
     * @param obj 
     */
    import(obj: any) { 
        Object.assign(this.userData.enters, obj.userData.enters)
        Object.assign(this.userData.outputs, obj.userData.outputs)
        delete obj.userData.outputs
        delete obj.userData.enters

        this.userData = {
            ...this.userData,
            ...obj.userData
        }

        this.uuid = obj.uuid
        this.name = obj.name

        this.setStyle(obj.position)

        if(this.userData.enters){
            for (const key in this.userData.enters) {
                if (Object.prototype.hasOwnProperty.call(this.userData.enters, key)) {
                    if(this.inputModels.enters[key]) this.inputModels.enters[key].setValue((this.userData as any).enters[key])
                }
            }
        }
        if(this.userData.outputs){
            for (const key in this.userData.outputs) {
                if (Object.prototype.hasOwnProperty.call(this.userData.outputs, key)) {
                    if(this.inputModels.outputs[key]) this.inputModels.outputs[key].setValue((this.userData as any).outputs[key])
                }
            }
        }
    }

    importSuccess(obj: any){}

    /**
     * 导出
     * @returns 
     */
    export() {
        const info:any =  {
            uuid: this.uuid,
            ClassName: this.ClassName,
            userData: this.userData,
            name: this.name,
            position:{
                left: this._style.left,
                top: this._style.top
            },
            outputs: {},
            enters: {}
        }

        Object.keys(this.outputs).forEach(k=>{
            info.outputs[k] = this.outputs[k].export()
        })
        Object.keys(this.enters).forEach(k=>{
            info.enters[k] = this.enters[k].export()
        })
        return info
    }

    onMove(e:any) {
        Object.keys(this.enters).forEach(key => this.enters[key].update())
        Object.keys(this.outputs).forEach(key => this.outputs[key].update())
    }

    addEnter(name: string, linkNode: LinkNode) {
        this.enters[name] = linkNode
        linkNode.name = name
    }

    addOutput(name: string, linkNode: LinkNode) {
        this.outputs[name] = linkNode
        linkNode.name = name
    }

    getLinkValue(){
        
    }

    getPosition(){
        return{
            left: Number((this._style.left || '').substring(0, this._style.left.length - 2)),
            top: Number((this._style.top || '').substring(0, this._style.top.length - 2))
        }
    }

    delete(){
        Object.values(this.enters).forEach(item=> item.closeAll())
        Object.values(this.outputs).forEach(item=> item.closeAll())

        this.graphView?.remove(this)
    }

    closeError(){
        if(this.error) {
            this.error.removeTo()
            delete this.error
        }
    }

    setError(msg: string){
        this.closeError()

        const err = new AbsoluteLayout()
        .borderRadius("8px")
        .pointerEvents("none")
        this.appendChild(err)
        err.position("absolute", "0", "0").width("100%").height("100%").border("solid 3px #f44336").boxsizing("border-box")
        const text = new Text(msg)
        err.appendChild(text)

        text.position("absolute").left("50%").bottom("calc(100% + 10px)").top("unset")
        // .whiteSpace("nowrap")
        .padding("5px 0px")
        .minWidth("110%")
        .textAlign("center")
        .transform2D().translate("-50%", "0").create()
        .background("#f44336")
        .borderRadius("10px")
        .pointerEvents("all")

        const div = new CustomElement()
        div.width("10px").height("10px").background("#f44336")
        .transform2D().translate("-50%", "0").rotate(45).create()
        .position("absolute").left("50%").bottom("-5px").top("unset")

        text.appendChild(div)

        this.error = err
    }
}
